package il.ac.tau.arielgue.outxing.contexts;

import il.ac.tau.arielgue.outxing.agents.OutXingOrganism;
import il.ac.tau.arielgue.outxing.utils.Params;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;

import proevolution.agents.Organism;
import repast.simphony.engine.schedule.ScheduleParameters;
import repast.simphony.engine.schedule.ScheduledMethod;
import repast.simphony.random.RandomHelper;

/**
 * 
 * @author arielgue
 * 
 */
public class DemeSubContext extends AbstractOutXingSubContext {

	//private static final double MIGRATION_THRESHOLD = 0.5; //replaced by getMigrationProbability()
	private static final long serialVersionUID = -4520746256274516063L;

	public DemeSubContext() {
		super();
	}

	public DemeSubContext(Object name, Object typeID) {
		super(name, typeID);
	}

	public DemeSubContext(Object name) {
		super(name);
	}

	/**
	 * called by master context. chooses organisms and transfers them to the
	 * pool. </br>TODO document logic.
	 */
	public Collection<Organism> outcross() {
		double density = calculateDensity();
		double average = 0;

		ArrayList<Organism> organisms = new ArrayList<Organism>(getOrganisms());
		for (int i = 0; i < organisms.size(); i++) {
			OutXingOrganism oo = (OutXingOrganism) organisms.get(i);
			double tendency = oo.getOutXingTendency(Params
					.getOutXingTendecySlope(), Params.getOutXingTendenctMaxX(),
					Params.getOutXingTendenctMidX(), density);
			average = (i * average + tendency) / (i + 1);
		}

		Collection<Organism> outXers = new HashSet<Organism>();
		double feedback = (1 + Math.exp(-Params
				.getOutXingTendencyFeedbackSlope()
				* (1 - average)))
				* density; // so that as average is
		// higher there there will be a stronger positive feedback effect

		for (int i = 0; i < organisms.size(); i++) {
			OutXingOrganism oo = (OutXingOrganism) organisms.get(i);
			if (RandomHelper.nextDouble() < oo.getOutXingTendency(Params
					.getOutXingTendecySlope(), Params.getOutXingTendenctMaxX(),
					Params.getOutXingTendenctMidX(), feedback)) {

				oo.modifyFitness(1 - Params.getOutXingFitnessCost());
				oo.setMovedToPool(1);
				remove(oo);
				outXers.add(oo);
			}
		}

		return outXers;
	}

	/**
	 * called by master context
	 * 
	 * @return a collection of organisms destined to migrate from this deme to
	 *         another deme.
	 */
	public Collection<Organism> migrate() {
		Collection<Organism> migrators = new HashSet<Organism>();

		for (Organism o : getOrganisms()) {
			if (RandomHelper.nextDouble() < getMigrationProbability()) {
				remove(o);
				((OutXingOrganism) o).modifyFitness(1 - Params
						.getMigrationFitnessCost());
				migrators.add(o);
			}
		}
		return migrators;
	}

	private double getMigrationProbability() {
		return Params.getMigrationProbability();
	}

	@Override
	public String serialize() {
		return null;
	}

	/**
	 * @return the density of the population of organisms in this deme
	 */
	protected double calculateDensity() {
		return (double) size() / getMaxPopulationSize();
	}

}
